package com.ibm.cps.sql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.statement.select.AllColumns;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectItemVisitor;

import com.ibm.cps.sql.expression.IdentifierNode;
import com.ibm.cps.sql.expression.ValueExpression;
import com.ibm.cps.sql.netsfparser.ValueExpressionParser;
import com.ibm.util.ErrorCode;
import com.ibm.util.exception.CPSException;

public class SelectItemParser {

	/**
	 * get alias map for each select items 
	 * @param selectItems
	 * @return
	 * @throws CPSException
	 */
	public SelectItemDescriptor parse(List<SelectItem> selectItems)
			throws CPSException {
		try{
			SelectAllDetectableVisitor visitor = new SelectAllDetectableVisitor();
			for (SelectItem item : selectItems) {
				item.accept(visitor);
			}
			return new SelectItemDescriptor(visitor.isSelectAll, visitor.selectAllTables, visitor.fields);
		}catch(IllegalArgumentException parseFailed){
			throw new CPSException(ErrorCode.INPUT_ERROR, parseFailed.getMessage(), parseFailed);
		}
	}
	
	private static class SelectAllDetectableVisitor implements SelectItemVisitor{
		private final ValueExpressionParser parser = new ValueExpressionParser();
		private final Map<String, ValueExpression> fields = new HashMap<>();
		private final List<String> selectAllTables = new ArrayList<>();
		private boolean isSelectAll = false;
		
		@Override
		public void visit(AllColumns arg0) {
			this.isSelectAll = true;
		}

		@Override
		public void visit(AllTableColumns arg0) {
			this.selectAllTables.add(arg0.getTable().getFullyQualifiedName());
		}

		@Override
		public void visit(SelectExpressionItem selectItem) {
			selectItem.getExpression().accept(parser);
			ValueExpression expression = parser.getExpression();
			Alias alias = selectItem.getAlias();
			if(alias == null){
				if(expression instanceof IdentifierNode){
					String fieldName = ((IdentifierNode)expression).getName();
					if(fields.containsKey(fieldName)){
						throw new IllegalArgumentException("ambigious select item alias " + fieldName + " used in " + selectItem);
					}
					fields.put(fieldName, expression);
				}else{
					throw new IllegalArgumentException("Alias is required for select item: " + selectItem);
				}
			}else{
				fields.put(alias.getName(), expression);
			}
		}
	}

}
